/*
Go里面实现一个AOP
资料 https://mp.weixin.qq.com/s/ACtjNgUKx-GQf52ujUes_w
*/

package main

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
)

const before = "fmt.Println(\"before\")"

func main() {
	appendBefore()
}

// 在execute方法执行之前添加一条打印before的语句
func appendBefore() {
	var src = `
package main
import "fmt"
func main() {
	fmt.Println(execute("哈哈哈"))
}
func execute(name string) string {
	return name
}
`
	fset := token.NewFileSet()
	// 这里file就是一个AST对象
	file, err := parser.ParseFile(fset, "", src, parser.ParseComments)

	exprInsert, err := parser.ParseExpr(before)
	if err != nil {
		panic(err)
	}

	decls := make([]ast.Decl, 0, len(file.Decls))

	for _, decl := range file.Decls {
		fd, ok := decl.(*ast.FuncDecl)
		if ok {
			if fd.Name.Name == "execute" {
				stats := make([]ast.Stmt, 0, len(fd.Body.List)+1)
				stats = append(stats, &ast.ExprStmt{
					X: exprInsert,
				})
				stats = append(stats, fd.Body.List...)
				fd.Body.List = stats
				decls = append(decls, fd)
				continue
			} else {
				decls = append(decls, decl)
			}
		} else {
			decls = append(decls, decl)
		}
	}

	file.Decls = decls

	var cfg printer.Config
	var buf bytes.Buffer
	cfg.Fprint(&buf, fset, file)
	fmt.Printf(buf.String())
}
